/*   Carina
 *   Copyright (C) 2009 2010 2011  Zdravko Velinov
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <QFileDialog>
#include <QMessageBox>
#include <QUrl>

#include "carina-editor.hh"
#include "ced-about.hh"
#include "scene-explorer.hh"

#include "ce-app.hh"

#include "carina/ces-loader.hh"

// FIXME: lose of focus and movement

CarinaEditor::CarinaEditor()
    :   m_Rotate(false),
        m_Velocity(1e-8f),
        m_Movement(0)
{
    m_CEdWindow.setupUi(this);

    m_Converters["ces"] = make_aligned_shared<CESConverter>();

    m_Renderer = m_CEdWindow.CEView->getRenderer();
    m_Renderer->setClearColor(Vector4(0.0f, 0.7f, 1.0f, 0.0f));
    m_ResMgr = make_aligned_shared<ResourceManager>(m_Renderer, CEApplication::getResourceCache());
    m_Lighting = make_aligned_shared<LightingManager>(m_ResMgr);
    m_Scene = make_aligned_shared<Scene>(m_Lighting, m_ResMgr, m_CEdWindow.CEView->width(), m_CEdWindow.CEView->height());
    m_Scene->getCamera()->translate(Vector3(0.0f, 0.0f, 10.0f));
    m_World = make_aligned_shared<WorldManager>();
    m_NodeMgr = make_aligned_shared<NodeManager>(m_World, m_Lighting);
    m_Axis = make_aligned_unique<Axis>(m_ResMgr);

    QAction* lscreate_action;
    for(LightingManager::const_iterator i = m_Lighting->getFactoryBegin(); i != m_Lighting->getFactoryEnd(); ++i)
        if(i->first != "Ambient light")
        {
            lscreate_action = new QAction(QString(i->first.c_str()), this);
            connect(lscreate_action, SIGNAL(triggered(bool)), this, SLOT(on_action_CreateLightSource_triggered(bool)));
            m_CEdWindow.menuLight_source->addAction(lscreate_action);
        }

    m_SceneExp = CE_ALLOCATE(SceneExplorer)(m_CurrentFile, m_Lighting, m_NodeMgr, m_Scene);
    addDockWidget(Qt::LeftDockWidgetArea, m_SceneExp);
    m_SceneExp->setVisible(false);
    m_CEdWindow.menu_View->addAction(m_SceneExp->toggleViewAction());

    m_PropEdit = CE_ALLOCATE(PropertyEditor);
    addDockWidget(Qt::RightDockWidgetArea, m_PropEdit);
    m_PropEdit->setVisible(false);
    m_CEdWindow.menu_View->addAction(m_PropEdit->toggleViewAction());
    connect(m_SceneExp, SIGNAL(editableSelected(EditablePtr)), m_PropEdit, SLOT(setEditable(EditablePtr)));

    m_EntInsp = CE_ALLOCATE(EntityInspector)(m_CurrentFile, m_World, m_ResMgr);
    addDockWidget(Qt::RightDockWidgetArea, m_EntInsp);
    m_EntInsp->setVisible(false);
    m_CEdWindow.menu_View->addAction(m_EntInsp->toggleViewAction());
    connect(m_EntInsp, SIGNAL(createModelInstance(const string&)), m_SceneExp, SLOT(createModelInstance(const string&)));
}

CarinaEditor::~CarinaEditor()
{
}

void CarinaEditor::on_CEView_rendering()
{
    m_Timer.update();
    float diff = static_cast<float>(m_Timer.getElapsedTime());

    CameraPtr cam = m_Scene->getCamera();
    if(m_Movement)
    {
        if(m_Movement & M_FORWARD)
            cam->translate(- cam->getForwardVector() * m_Velocity * diff);
        if(m_Movement & M_BACKWARD)
            cam->translate(cam->getForwardVector() * m_Velocity * diff);
        if(m_Movement & M_RIGHT)
            cam->translate(cam->getLeftVector() * m_Velocity * diff);
        if(m_Movement & M_LEFT)
            cam->translate(- cam->getLeftVector() * m_Velocity * diff);
    }

    m_Scene->draw();
    m_Axis->draw(m_Scene->getCamera());
}

void CarinaEditor::on_CEView_keyDown(int key)
{
    switch(key)
    {
    case Qt::Key_W:
        m_Movement |= M_FORWARD; break;
    case Qt::Key_A:
        m_Movement |= M_LEFT; break;
    case Qt::Key_S:
        m_Movement |= M_BACKWARD; break;
    case Qt::Key_D:
        m_Movement |= M_RIGHT; break;
    }
}

void CarinaEditor::on_CEView_keyUp(int key)
{
    switch(key)
    {
    case Qt::Key_W:
        m_Movement &= ~M_FORWARD; break;
    case Qt::Key_A:
        m_Movement &= ~M_LEFT; break;
    case Qt::Key_S:
        m_Movement &= ~M_BACKWARD; break;
    case Qt::Key_D:
        m_Movement &= ~M_RIGHT; break;
    }
}

void CarinaEditor::on_CEView_mouseMoved(int x, int y, int xrel, int yrel)
{
    if(m_Rotate)
        m_Scene->getCamera()->rotate((float)xrel, (float)yrel);
}

void CarinaEditor::on_CEView_resized(size_t x, size_t y)
{
    m_Scene->resize(x, y);
}

void CarinaEditor::on_CEView_mousePressed(int button)
{
    if(button == Qt::MiddleButton)
        m_Rotate = true;
}

void CarinaEditor::on_CEView_mouseReleased(int button)
{
    if(button == Qt::MiddleButton)
        m_Rotate = false;
}

void CarinaEditor::on_action_New_triggered(bool checked)
{
    m_EntInsp->clear();
    m_SceneExp->clear();
}

void CarinaEditor::on_action_Open_triggered(bool checked)
{
    QString filename(QFileDialog::getOpenFileName(this, tr("Open"), "", tr("Carina Engine Scene Files (*.ces)")));
    if(filename.isNull())
        return;
    QString ext(filename.section('.', -1, -1).toLower());
    CESConverterMap::iterator i = m_Converters.find(ext.toStdString());
    if(i == m_Converters.end())
    {
        QMessageBox::critical(this, "Unsupported", "Unsupported file format: " + ext);
        return;
    }

    m_CurrentFile.clear();
    if(!i->second->open(m_CurrentFile, filename.toStdString()))
    {
        m_CurrentFile.clear();
        QMessageBox::critical(this, "Error", "The application has failed to convert or load the following file to *.ces: " + filename); 
        return;
    }

    if(!m_EntInsp->reload())
    {
        on_action_New_triggered(checked);
        QMessageBox::critical(this, "Error", "The application has failed to load the resources from the following file: " + filename); 
        return;
    }

    if(!m_SceneExp->reload())
    {
        on_action_New_triggered(checked);
        QMessageBox::critical(this, "Error", "The application has failed to load the scene from the following file: " + filename); 
        return;
    }

    m_FileName = filename.toStdString();
}

void CarinaEditor::on_action_Save_triggered(bool checked)
{
    if(m_FileName.empty())
        on_actionSave_As_triggered(checked);
    if(!m_CurrentFile.save(m_FileName))
    {
        QMessageBox::critical(this, "Error", "The application has failed to save the following file: " + QString(m_FileName.c_str()));
        return;
    }
}

void CarinaEditor::on_actionSave_As_triggered(bool checked)
{
    QString filename(QFileDialog::getSaveFileName(this, "Save As", QString(), "Carina Engine Scene Files (*.ces)"));
    if(filename.isEmpty())
        return;
    if(!m_CurrentFile.save(filename.toStdString()))
    {
        QMessageBox::critical(this, "Error", "The application has failed to save the following file: " + filename);
        return;
    }
    m_FileName = filename.toStdString();
}

void CarinaEditor::on_actionAbout_Carina_Editor_triggered(bool checked)
{
    CEdAbout about_dialog(this);
    about_dialog.exec();
}

void CarinaEditor::on_action_Quit_triggered(bool checked)
{
    exit(EXIT_SUCCESS);
}

void CarinaEditor::on_action_CreateLightSource_triggered(bool checked)
{
    QAction* s = static_cast<QAction*>(this->sender());
    if(s)
        m_SceneExp->createLightSource(s->iconText().toStdString());
}
